package org.mozhu.mboot.core.data;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.collections.CollectionUtils;
import org.mozhu.mboot.core.data.filter.GroupSearchFilter;
import org.mozhu.mboot.core.data.filter.Operator;
import org.mozhu.mboot.core.data.filter.SearchFilter;
import org.mozhu.mboot.core.data.filter.SimpleSearchFilter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 查询条件（包括分页和排序）
 * </p>
 */

public final class SearchRequest extends Searchable {

	private final Map<String, SearchFilter> searchFilterMap = Maps.newHashMap();
	/**
	 * 使用这个的目的是保证拼sql的顺序是按照添加时的顺序
	 */
	private final List<SearchFilter> searchFilters = Lists.newArrayList();

	private Pageable page;

	private Sort sort;

	public SearchRequest(final Map<String, Object> searchParams) {
		this(searchParams, null, null);
	}

	public SearchRequest() {
		this(null, null, null);
	}

	public SearchRequest(Sort sort) {
		this.sort = sort;
	}

	public SearchRequest(Pageable page) {
		this.page = page;
	}

	public SearchRequest(Pageable page, Sort sort) {
		this.page = page;
		this.sort = sort;
	}

	public SearchRequest(final Map<String, Object> searchParams, final Pageable page) {
		this(searchParams, page, null);
	}

	public SearchRequest(final Map<String, Object> searchParams, final Sort sort) {
		this(searchParams, null, sort);
	}

	public SearchRequest(final Map<String, Object> searchParams, final Pageable page, final Sort sort) {
		toSearchFilters(searchParams);
		merge(sort, page);
	}

	private void toSearchFilters(final Map<String, Object> searchParams) {
		if (searchParams == null || searchParams.size() == 0) {
			return;
		}
		addSearchFilters(SearchFilters.parse(searchParams).values());
	}

	@Override
	public Searchable addSearchParam(final String key, final Object value) {
		addSearchFilter(SearchFilters.createFilter(key, value));
		return this;
	}

	@Override
	public Searchable addSearchParams(Map<String, Object> searchParams) {
		toSearchFilters(searchParams);
		return this;
	}

	@Override
	public Searchable addSearchFilter(final String searchProperty, final Operator operator, final Object value) {
		SearchFilter searchFilter = SearchFilters.createFilter(searchProperty, operator, value);
		return addSearchFilter(searchFilter);
	}

	@Override
	public Searchable addSearchFilter(final String searchProperty, final Operator operator, final Object[] value) {
		SearchFilter searchFilter = SearchFilters.createFilter(searchProperty, operator, value);
		return addSearchFilter(searchFilter);
	}

	@Override
	public Searchable addSearchFilter(List<String> propertyNames, Operator operator, Object value) {
		SearchFilter searchFilter = SearchFilters.createFilter(propertyNames, operator, value);
		return addSearchFilter(searchFilter);
	}

	@Override
	public Searchable addSearchFilter(SearchFilter searchFilter) {
		if (searchFilter == null) {
			return this;
		}
		if (searchFilter instanceof SimpleSearchFilter) {
			SimpleSearchFilter searchFilters = (SimpleSearchFilter) searchFilter;
			String key = searchFilters.getKey();
			searchFilterMap.put(key, searchFilters);
		}
		int index = searchFilters.indexOf(searchFilter);
		if (index != -1) {
			searchFilters.set(index, searchFilter);
		} else {
			searchFilters.add(searchFilter);
		}
		return this;

	}

	@Override
	public Searchable addSearchFilters(Collection<? extends SearchFilter> searchFilters) {
		if (CollectionUtils.isEmpty(searchFilters)) {
			return this;
		}
		for (SearchFilter searchFilter : searchFilters) {
			addSearchFilter(searchFilter);
		}
		return this;
	}

	@Override
	public Searchable or(final SearchFilter... searchFilters) {
		addSearchFilter(SearchFilters.or(searchFilters));
		return this;
	}

	@Override
	public Searchable and(final SearchFilter... searchFilters) {
		addSearchFilter(SearchFilters.and(searchFilters));
		return this;
	}

	@Override
	public Searchable removeSearchFilter(final String propertyName, final Operator operator) {
		this.removeSearchFilter(SearchFilters.getKey(propertyName, operator));
		return this;
	}

	@Override
	public Searchable removeSearchFilter(final String key) {
		if (key == null) {
			return this;
		}
		SearchFilter searchFilter = searchFilterMap.remove(key);
		if (searchFilter == null) {
			return this;
		}
		searchFilters.remove(searchFilter);
		return this;
	}

	@Override
	public Searchable setPage(final Pageable page) {
		merge(sort, page);
		return this;
	}

	@Override
	public Searchable setPage(int pageNumber, int pageSize) {
		merge(sort, PageRequest.of(pageNumber, pageSize));
		return this;
	}

	@Override
	public Searchable addSort(final Sort sort) {
		merge(sort, page);
		return this;
	}

	@Override
	public Searchable addSort(final Sort.Direction direction, final String property) {
		merge(new Sort(direction, property), page);
		return this;
	}

	@Override
	public Collection<SearchFilter> getSearchFilters() {
		return Collections.unmodifiableCollection(searchFilters);
	}

	@Override
	public boolean hasSearchFilter() {
		return searchFilters.size() > 0;
	}

	@Override
	public boolean hashSort() {
		return this.sort != null && this.sort.iterator().hasNext();
	}

	@Override
	public boolean hasPageable() {
		return this.page != null && this.page.getPageSize() > 0;
	}

	@Override
	public void removeSort() {
		this.sort = null;
		if (this.page != null) {
			this.page = PageRequest.of(page.getPageNumber(), page.getPageSize(), null);
		}
	}

	@Override
	public void removePageable() {
		this.page = null;
	}

	@Override
	public Pageable getPage() {
		return page;
	}

	@Override
	public Sort getSort() {
		return sort;
	}

	@Override
	public boolean containsSearchKey(String key) {
		boolean contains = searchFilterMap.containsKey(key);
		if (contains) {
			return true;
		}
		// 否则检查其中的or 和 and
		return containsSearchKey(searchFilters, key);
	}

	private boolean containsSearchKey(List<SearchFilter> searchFilters, String key) {
		boolean contains = false;
		for (SearchFilter searchFilter : searchFilters) {
			if (searchFilter instanceof GroupSearchFilter) {
				GroupSearchFilter filter = (GroupSearchFilter) searchFilter;
				contains = containsSearchKey(filter.getSearchFilters(), key);
			}
			if (searchFilter instanceof SimpleSearchFilter) {
				SimpleSearchFilter filter = (SimpleSearchFilter) searchFilter;
				contains = filter.getKey().equals(key) || filter.getPropertyName().equals(key);
			}
			if (contains) {
				return true;
			}
		}
		return contains;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T getValue(String key) {
		SearchFilter searchFilter = searchFilterMap.get(key);
		if (searchFilter == null) {
			return null;
		}
		if (searchFilter instanceof SimpleSearchFilter) {
			SimpleSearchFilter filter = (SimpleSearchFilter) searchFilter;
			return (T) filter.getValue();
		}
		return null;
	}

	private void merge(Sort sort, Pageable page) {
		if (sort == null) {
			sort = this.sort;
		}
		if (page == null) {
			page = this.page;
		}

		// 合并排序
		if (sort == null) {
			this.sort = page != null ? page.getSort() : null;
		} else {
			this.sort = (page != null ? sort.and(page.getSort()) : sort);
		}
		// 把排序合并到page中
		if (page != null) {
			this.page = PageRequest.of(page.getPageNumber(), page.getPageSize(), this.sort);
		} else {
			this.page = null;
		}
	}

	@Override
	public String toString() {
		return "SearchRequest{" + "searchFilterMap=" + searchFilterMap + ", page=" + page + ", sort=" + sort + '}';
	}

}
